#include <QtCore>
#include <QtGui>
// #include <QtDebug>

#include "ImageResizer.h"

using namespace Ui;

ImageResizer::ImageResizer(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{
	ui->setupUi(this);
	ui->dirPath->setText(getenv("USERPROFILE"));
	ui->pixels->setEditText("640");

	connect(ui->changeButton, SIGNAL(clicked()), this, SLOT(chooseDir()));

	connect(ui->actionAbout, SIGNAL(triggered()), this, SLOT(about()));
	connect(ui->actionAboutQt, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
	connect(ui->actionSaveSettings, SIGNAL(toggled(bool)), this, SLOT(enableSavingSettings(bool)));
	connect(ui->actionAlwaysOnTop, SIGNAL(toggled(bool)), this, SLOT(enableAlwaysOnTop(bool)));

	timer = new QTimer(this);
	timer->setInterval(1000);
	connect(timer, SIGNAL(timeout()), this, SLOT(processQueue()));
	timer->start();

	this->loadSettings();

	setAcceptDrops(true);
}

ImageResizer::~ImageResizer()
{
	if (ui->actionSaveSettings->isChecked())
		this->saveSettings();
}

void ImageResizer::loadSettings()
{
	QSettings *settings;
	if (QFileInfo(qApp->applicationName() + ".ini").exists())
		settings = new QSettings(qApp->applicationName() + ".ini", QSettings::IniFormat, this);
	else
		settings = new QSettings(QSettings::IniFormat, QSettings::UserScope, qApp->organizationName(), qApp->applicationName(), this);
	ui->actionSaveSettings->setChecked(settings->value("General/save_settings", false).toBool());
	ui->actionAlwaysOnTop->setChecked(settings->value("General/always_on_top", false).toBool());
	ui->actionProcessDirsRecursively->setChecked(settings->value("General/process_dirs_recursively", false).toBool());
	ui->actionShowPreview->setChecked(settings->value("General/show_preview", true).toBool());
	ui->shortSideBased->setChecked(settings->value("Image/short_side_based", false).toBool());
	ui->pixels->setEditText(QString("%1").arg(settings->value("Image/pixels", 640).toInt()));
	ui->quality->setValue(settings->value("Image/quality", ui->quality->value()).toInt());
	ui->outputToSubDir->setChecked(settings->value("Output/output_to_sub_dir", false).toBool());
	ui->outputToSameDir->setChecked(settings->value("Output/output_to_same_dir", false).toBool());
	ui->outputToDir->setChecked(settings->value("Output/output_to_dir", false).toBool());
	ui->outputToClipboard->setChecked(settings->value("Output/output_to_clipboard", false).toBool());
	ui->subDirName->setText(settings->value("Output/sub_dir_name", ui->subDirName->text()).toString());
	ui->fix->setText(settings->value("Output/fix", ui->fix->text()).toString());
	ui->fixType->setCurrentIndex(settings->value("Output/fix_type", ui->fixType->currentIndex()).toInt());
	ui->dirPath->setText(settings->value("Output/dir_path", ui->dirPath->text()).toString());
	delete settings;
}

void ImageResizer::saveSettings()
{
	QSettings *settings;
	if (QFileInfo(qApp->applicationName() + ".ini").exists())
		settings = new QSettings(qApp->applicationName() + ".ini", QSettings::IniFormat, this);
	else
		settings = new QSettings(QSettings::IniFormat, QSettings::UserScope, qApp->organizationName(), qApp->applicationName(), this);
	settings->setValue("General/save_settings", ui->actionSaveSettings->isChecked());
	settings->setValue("General/always_on_top", ui->actionAlwaysOnTop->isChecked());
	settings->setValue("General/process_dirs_recursively", ui->actionProcessDirsRecursively->isChecked());
	settings->setValue("General/show_preview", ui->actionShowPreview->isChecked());
	settings->setValue("Image/short_side_based", ui->shortSideBased->isChecked());
	settings->setValue("Image/pixels", ui->pixels->currentText().toInt());
	settings->setValue("Image/quality", ui->quality->value());
	settings->setValue("Output/output_to_sub_dir", ui->outputToSubDir->isChecked());
	settings->setValue("Output/output_to_same_dir", ui->outputToSameDir->isChecked());
	settings->setValue("Output/output_to_dir", ui->outputToDir->isChecked());
	settings->setValue("Output/output_to_clipboard", ui->outputToClipboard->isChecked());
	settings->setValue("Output/sub_dir_name", ui->subDirName->text());
	settings->setValue("Output/fix", ui->fix->text());
	settings->setValue("Output/fix_type", ui->fixType->currentIndex());
	settings->setValue("Output/dir_path", ui->dirPath->text());
	delete settings;
}

void ImageResizer::about()
{
	QString message = tr("%1\nby %2\nVersion: %3\nQt Version: %4\nCompiled with gcc %5.%6.%7 on %8")
					  .arg(qApp->applicationName())
					  .arg(qApp->organizationName())
					  .arg(qApp->applicationVersion())
					  .arg(QT_VERSION_STR)
					  .arg(__GNUC__)
					  .arg(__GNUC_MINOR__)
					  .arg(__GNUC_PATCHLEVEL__)
					  .arg(__DATE__);
	QMessageBox::information(this, tr("About"), message);
}

void ImageResizer::enableSavingSettings(bool b)
{}

void ImageResizer::enableAlwaysOnTop(bool enabled)
{
	if (enabled)
		this->setWindowFlags(this->windowFlags() | Qt::WindowStaysOnTopHint);
	else
		this->setWindowFlags(this->windowFlags() ^ Qt::WindowStaysOnTopHint);
	this->show();
}

void ImageResizer::chooseDir()
{
	QString path = QFileDialog::getExistingDirectory(this);
	if (!path.isEmpty())
		ui->dirPath->setText(path);
}

void ImageResizer::dragEnterEvent(QDragEnterEvent *e)
{
	e->acceptProposedAction();
}

void ImageResizer::dragLeaveEvent(QDragLeaveEvent *e)
{
	e->accept();
}

void ImageResizer::dragMoveEvent(QDragMoveEvent *e)
{
	e->acceptProposedAction();
}

void ImageResizer::dropEvent(QDropEvent *e)
{
	const QMimeData *mime = e->mimeData();

	if (!mime->hasUrls())
		return;

	QList<QUrl> urls = mime->urls();

	// confirm resizing multiple files
	if (1 < urls.size() && QMessageBox::warning(this, tr("Warning"), tr("Multiple files added. Each of them is going to be resized. Continue?"), QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel) == QMessageBox::Cancel)
		return;

	for (int i = 0; i != urls.size(); ++i)
	{
		const QString path = urls.at(i).toString().remove("file:///");
		queue.enqueue(path);
	}

	e->acceptProposedAction();
}

void ImageResizer::processQueue()
{
	timer->stop();

	QProgressDialog progress(tr("Resizing files..."), tr("Abort"), 0, queue.count(), this);
	progress.setWindowModality(Qt::WindowModal);
	int count = queue.count();

	QTime time;
	time.start();

	while (!queue.isEmpty())
	{
		progress.show();
		progress.setLabelText(queue.head());
		if (progress.wasCanceled() || this->processPath(queue.dequeue()) == false)
		{
			QMessageBox::information(this, tr("Canceled"), tr("Resizing images canceled."));
			queue.clear();
			timer->start();
			return;
		}
		progress.setValue(count - queue.count());

		if (queue.isEmpty())	QMessageBox::information(this, tr("Done"), tr("Resizing images finished.\nElapsed time: %1secs").arg(time.elapsed() / 1000));
	}

	timer->start();
}

bool ImageResizer::processPath(const QString &path)
{
	QFileInfo pathInfo(path);
	if (pathInfo.isDir())
	{
		QDir dir(path);
		QStringList entries;
		if (ui->actionProcessDirsRecursively->isChecked())
			entries = dir.entryList(QDir::Dirs | QDir::Files);
		else
			entries = dir.entryList(QDir::Files);

		QProgressDialog progress(tr("Processing a directory..."), tr("Abort"), 0, entries.count(), this);
		progress.setWindowModality(Qt::WindowModal);
		progress.show();

		for (int i = 0; i != entries.count(); ++i)
		{
			if (progress.wasCanceled())	return false;
			progress.setLabelText(entries.at(i));
			if (this->processPath(dir.absoluteFilePath(entries.at(i))) == false)	return false;
			progress.setValue(i);
		}
	}
	else if (pathInfo.isFile())
	{
		if (QFileInfo(outputPath(path)).exists())
		{
			QMessageBox::StandardButton r = QMessageBox::question(this, tr("Saving Image"), tr("Destination file exists.\nOverwrite?"), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Cancel);
			switch (r)
			{
				case QMessageBox::No:	return true;
				case QMessageBox::Cancel:	return false;
				case QMessageBox::Yes:
				default:
					break;
			}
		}

		return this->resizeAndSaveImage(path, outputPath(path));
	}

	return true;
}

QString ImageResizer::outputPath(const QString &source) const
{
	QFileInfo fi(source);
	const QString filename = fi.fileName();	// filename.ext
	const QString dirPath = fi.path();
	const QString ext = fi.suffix();	// extension part of filename

	if (ui->outputToSameDir->isChecked())
	{
		if (ui->fixType->currentText() == tr("Prefix"))
			return dirPath + "/" + ui->fix->text() + filename;
		else
			return dirPath + "/" + filename.left(filename.lastIndexOf('.')) + ui->fix->text() + "." + ext;
	}
	else if (ui->outputToSubDir->isChecked())
	{
		QDir(dirPath).mkdir(ui->subDirName->text());
		return dirPath + "/" + ui->subDirName->text() + "/" + filename;
	}
	else if (ui->outputToDir->isChecked())
		return ui->dirPath->text() + "/" + filename;

	return QString();
}

bool ImageResizer::resizeAndSaveImage(const QString &path, const QString &dest)
{
	QPixmap pix(path);
	if (pix.isNull())
	{
		QMessageBox::information(this, tr("Error"), tr("Can't load image: %1\nYou don't have permission to access this file or the image format is not supported.").arg(path));
		return false;
	}

	/*
	  plugins\imageformatsのDLLがないとJPEGも読み込めないので注意
	 */
	// update preview
	if (ui->actionShowPreview->isChecked())
		ui->preview->setPixmap(pix.scaled(ui->preview->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
	// no: 35014
	// thread: 40716
	// non thread: 48734

	// generate shrinked pixmap
	const int w = pix.width(), h = pix.height();
	const int shrinkSize = ui->pixels->currentText().toInt();
	int nw, nh;
	if (ui->longSideBased->isChecked())
	{
		if (pix.height() < pix.width())	// landscape
			nw = shrinkSize, nh = h * shrinkSize / w;
		else	// portrait
			nw = w * shrinkSize / h, nh = shrinkSize;
		pix = pix.scaled(nw, nh, Qt::KeepAspectRatio, Qt::SmoothTransformation);
	}
	else	// short side based
	{
		if (pix.height() < pix.width())
			nw = w * shrinkSize / h, nh = shrinkSize;
		else
			nw = shrinkSize, nh = h * shrinkSize / w;
		pix = pix.scaled(nw, nh, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
	}

	QFileInfo fi(path);
	const QString dirPath = fi.path();
	const QString ext = fi.suffix();

	// output shrinked pixmap to dest
	if (ui->outputToClipboard->isChecked())
		QApplication::clipboard()->setPixmap(pix);
	else if (ui->outputToSubDir->isChecked())
	{
		QDir(dirPath).mkdir(ui->subDirName->text());
		return pix.save(dest, ext.toAscii(), ui->quality->value());
	}
	else
		return pix.save(dest, ext.toAscii(), ui->quality->value());

	return true;
}
